/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jitsi.impl.androidtray;

import android.app.*;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.support.v4.app.*;

import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.systray.*;
import net.java.sip.communicator.util.*;

import org.jitsi.R;
import org.jitsi.android.*;
import org.jitsi.android.gui.*;
import org.jitsi.android.gui.chat.*;
import org.jitsi.android.gui.util.*;
import org.jitsi.android.plugin.notificationwiring.*;

import java.util.*;

/**
 * Class manages displayed notification for given <tt>PopupMessage</tt>.
 *
 * @author Pawel Domas
 */
public class AndroidPopup {
    /**
     * The logger.
     */
    private final static Logger logger = Logger.getLogger(AndroidPopup.class);

    /**
     * Parent notifications handler
     */
    protected final NotificationPopupHandler handler;

    /**
     * Displayed <tt>PopupMessage</tt>.
     */
    protected PopupMessage popupMessage;

    /**
     * Timeout handler.
     */
    protected Timer timeoutHandler;

    /**
     * Notification id.
     */
    protected int id;

    /**
     * Optional contact if supplied by <tt>PopupMessage</tt>.
     */
    protected Contact contact;

    /**
     * Small icon used for this notification.
     */
    private int smallIcon;

    /**
     * Creates new instance of <tt>AndroidPopup</tt>.
     *
     * @param handler      parent notifications handler that manages displayed
     *                     notifications.
     * @param popupMessage the popup message that will be displayed by this
     *                     instance.
     */
    protected AndroidPopup(NotificationPopupHandler handler, PopupMessage popupMessage) {
        this.handler = handler; this.popupMessage = popupMessage;

        // Default Jitsi icon
        this.smallIcon = R.drawable.ic_stat_logo_white;

        // Null group is sharing general notification icon
        if (popupMessage.getGroup() == null) {
            // By default all notifications share Jitsi icon
            id = SystrayServiceImpl.getGeneralNotificationId();
        }
        else {
            // Generate separate notification
            id = (int) (System.currentTimeMillis() % Integer.MAX_VALUE);

            String group = popupMessage.getGroup();
            // Set message icon
            if (AndroidNotifications.MESSAGE_GROUP.equals(group)) {

                this.smallIcon = R.mipmap.incoming_message;
            }
            else if (AndroidNotifications.CALL_GROUP.equals(group)) {
                this.smallIcon = R.mipmap.missed_call;
            }
        }
        // Extract contained contact if any
        if (popupMessage.getTag() instanceof Contact) {
            this.contact = (Contact) popupMessage.getTag();
        }
    }

    /**
     * Returns displayed <tt>PopupMessage</tt>.
     *
     * @return displayed <tt>PopupMessage</tt>.
     */
    public PopupMessage getPopupMessage() {
        return popupMessage;
    }

    /**
     * Removes this notification.
     */
    public void removeNotification() {
        cancelTimeout();

        NotificationManager notifyManager = JitsiApplication.getNotificationManager();
        notifyManager.cancel(id);
    }

    /**
     * Returns <tt>true</tt> if this popup is related to given
     * <tt>ChatSession</tt>.
     *
     * @param session the <tt>ChatSession</tt> to check.
     * @return <tt>true</tt> if this popup is related to given
     * <tt>ChatSession</tt>.
     */
    public boolean isChatRelated(ChatSession session) {
        return contact != null && AndroidNotifications.MESSAGE_GROUP.equals(popupMessage.getGroup()) && session.getMetaContact().containsContact(contact);
    }

    /**
     * Returns notification id.
     *
     * @return notification id.
     */
    public int getId() {
        return id;
    }

    /**
     * Creates new <tt>AndroidPopup</tt> for given parameters.
     *
     * @param handler      notifications manager.
     * @param popupMessage the popup message that will be displayed by returned
     *                     <tt>AndroidPopup</tt>
     * @return new <tt>AndroidPopup</tt> for given parameters.
     */
    static public AndroidPopup createNew(NotificationPopupHandler handler, PopupMessage popupMessage) {
        return new AndroidPopup(handler, popupMessage);
    }

    /**
     * Tries to merge given <tt>PopupMessage</tt> with this instance. Will
     * return merged <tt>AndroidPopup</tt> or <tt>null</tt> otherwise.
     *
     * @param popupMessage the <tt>PopupMessage</tt> to merge.
     * @return merged <tt>AndroidPopup</tt> with given <tt>PopupMessage</tt> or
     * <tt>null</tt> otherwise.
     */
    public AndroidPopup tryMerge(PopupMessage popupMessage) {
        if (this.isGroupTheSame(popupMessage) && isContactTheSame(popupMessage)) {
            return mergePopup(popupMessage);
        }
        else {
            return null;
        }
    }

    /**
     * Merges this instance with given <tt>PopupMessage</tt>.
     *
     * @param popupMessage the <tt>PopupMessage</tt> to merge.
     * @return merge result for this <tt>AndroidPopup</tt> and given
     * <tt>PopupMessage</tt>.
     */
    protected AndroidPopup mergePopup(PopupMessage popupMessage) {
        // Timeout notifications are replaced
        /*if(this.timeoutHandler != null)
        {
            cancelTimeout();
            this.popupMessage = popupMessage;
            return this;
        }
        else
        {*/
        AndroidMergedPopup merge = new AndroidMergedPopup(this); merge.mergePopup(popupMessage);
        return merge;
        //}
    }

    /**
     * Checks whether <tt>Contact</tt> of this instance matches with given
     * <tt>PopupMessage</tt>.
     *
     * @param popupMessage the <tt>PopupMessage</tt> to check.
     * @return <tt>true</tt> if <tt>Contact</tt>s for this instance and given
     * <tt>PopupMessage</tt> are the same.
     */
    private boolean isContactTheSame(PopupMessage popupMessage) {
        return contact != null && contact.equals(popupMessage.getTag());
    }

    /**
     * Checks whether group of this instance matches with given
     * <tt>PopupMessage</tt>.
     *
     * @param popupMessage the <tt>PopupMessage</tt> to check.
     * @return <tt>true</tt> if group of this instance and given
     * <tt>PopupMessage</tt> are the same.
     */
    private boolean isGroupTheSame(PopupMessage popupMessage) {
        if (this.popupMessage.getGroup() == null) {
            return popupMessage.getGroup() == null;
        }
        else {
            return this.popupMessage.getGroup().equals(popupMessage.getGroup());
        }
    }

    /**
     * Returns message string that will displayed in single line notification.
     *
     * @return message string that will displayed in single line notification.
     */
    protected String getMessage() {
        return popupMessage.getMessage();
    }

    /**
     * Builds notification and returns the builder object which can be used to
     * extend the notification.
     *
     * @return builder object describing current notification.
     */
    NotificationCompat.Builder buildNotification() {
        Context ctx = JitsiApplication.getGlobalContext();
        NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx).setSmallIcon(smallIcon).setContentTitle(popupMessage.getMessageTitle()).setContentText(getMessage()).setAutoCancel(true)// will be cancelled once clciked
                .setVibrate(new long[]{}) // no vibration
                .setSound(null); // no sound

        // Big view comes with API11
        if (AndroidUtils.hasAPI(11)) {
            Resources res = JitsiApplication.getAppResources();

            // Preferred size
            int prefWidth = (int) res.getDimension(android.R.dimen.notification_large_icon_width);
            int prefHeight = (int) res.getDimension(android.R.dimen.notification_large_icon_height);

            // Use popup icon if any
            Bitmap iconBmp = null; byte[] icon = popupMessage.getIcon(); if (icon != null) {
                iconBmp = AndroidImageUtil.scaledBitmapFromBytes(icon, prefWidth, prefHeight);
            }

            // Set default avatar
            if (iconBmp == null && contact != null) {
                iconBmp = AndroidImageUtil.scaledBitmapFromResource(res, R.mipmap.avatar, prefWidth, prefHeight);
            }

            if (iconBmp != null) {
                if (iconBmp.getWidth() > prefWidth || iconBmp.getHeight() > prefHeight) {
                    iconBmp = Bitmap.createScaledBitmap(iconBmp, prefWidth, prefHeight, true);
                }

                builder.setLargeIcon(iconBmp);
            }

            // Build inbox style
            NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
            onBuildInboxStyle(inboxStyle); builder.setStyle(inboxStyle);
        }

        return builder;
    }

    /**
     * Returns the <tt>PendingIntent</tt> that should be trigger when user
     * clicks the notification.
     *
     * @return the <tt>PendingIntent</tt> that should be trigger by notification
     */
    public PendingIntent constructIntent() {
        Intent targetIntent = null; PopupMessage message = getPopupMessage();

        Object tag = message.getTag(); if (tag instanceof Contact) {
            Contact contact = (Contact) tag;
            MetaContact metaContact = AndroidGUIActivator.getContactListService().findMetaContactByContact(contact);
            if (metaContact == null) {
                logger.error("Meta contact not found for " + contact); return null;
            }

            String group = message != null ? message.getGroup() : null;

            if (AndroidNotifications.MESSAGE_GROUP.equals(group)) {
                targetIntent = ChatSessionManager.getChatIntent(metaContact);

                if (targetIntent == null) {
                    logger.error("Failed to create chat with " + metaContact);
                }
            }
            else {
                // TODO: add call history intent here
                targetIntent = null;
            }
        }

        // Displays popup message details when the notification is clicked
        if (message != null && targetIntent == null) {
            targetIntent = DialogActivity.getDialogIntent(JitsiApplication.getGlobalContext(), message.getMessageTitle(), message.getMessage());
        }

        if (targetIntent == null) {
            return null;
        }

        return PendingIntent.getActivity(JitsiApplication.getGlobalContext(), getId(), /*
                          * Must be unique for each, so use
                          * the notification id as the request code
                          */
                targetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    }

    /**
     * Method fired when large notification view using <tt>InboxStyle</tt> is
     * being built.
     *
     * @param inboxStyle the inbox style instance used for building large
     *                   notification view.
     */
    protected void onBuildInboxStyle(NotificationCompat.InboxStyle inboxStyle) {
        inboxStyle.addLine(popupMessage.getMessage());
        // Summary
        if (contact != null) {
            ProtocolProviderService pps = contact.getProtocolProvider(); if (pps != null) {
                inboxStyle.setSummaryText(pps.getAccountID().getDisplayName());
            }
        }
    }

    /**
     * Cancels the timeout if it exists.
     */
    protected void cancelTimeout() {
        // Remove timeout handler
        if (timeoutHandler != null) {
            logger.debug("Removing timeout from notification: " + id);

            timeoutHandler.cancel(); timeoutHandler = null;
        }
    }

    /**
     * Method called by notification manger when the notification is posted to
     * the tray.
     */
    public void onPost() {
        cancelTimeout(); long timeout = popupMessage.getTimeout(); if (timeout > 0) {
            logger.debug("Setting timeout " + timeout + " on notification: " + id);

            timeoutHandler = new Timer(); timeoutHandler.schedule(new TimerTask() {
                @Override
                public void run() {
                    handler.onTimeout(AndroidPopup.this);
                }
            }, timeout);
        }
    }
}
